package com.snail.common.excel.utils;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ConverterKeyBuild;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.snail.common.excel.annotation.Excel;
import com.snail.common.excel.handler.SnailCellWriteHandler;
import com.snail.common.excel.handler.SnailRowWriteHandler;
import com.snail.common.excel.handler.SnailSheetWriteHandler;
import com.snail.common.excel.listener.IExcelService;
import lombok.SneakyThrows;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URLEncoder;
import java.util.*;

/**
 * @Description: EasyExcel工具类
 * @Author: Snail
 * @CreateDate: 2023/8/15 9:35
 * @Version: V1.0
 */
public class EasyExcelUtils {

    /**
     * 下载模板
     *
     * @param response     response
     * @param fileName     文件名称
     * @param templatePath 模板路径
     */
    @SneakyThrows
    public static void downTemplate(HttpServletResponse response, String fileName, String templatePath) {
        //定义输出文件头信息输出类型
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        //文件名称
        fileName = URLEncoder.encode(fileName, "UTF-8");
        ClassPathResource classPathResource = new ClassPathResource(templatePath);
        //文件下载方式
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream()).
                withTemplate(classPathResource.getInputStream())
                .sheet().doWrite(ArrayList::new);
    }

    /**
     * 下载Excel导入模板
     *
     * @param response response
     * @param fileName 文件名
     * @param clazz    类
     */
    @SneakyThrows
    public static void downTemplate(HttpServletResponse response, String fileName, Class<?> clazz) {
        write(response, fileName, clazz, Collections.emptyList(), true);
    }

    /**
     * 导出数据到Excel
     *
     * @param response response
     * @param fileName 文件名
     * @param clazz    类
     * @param data     数据
     */
    @SneakyThrows
    public static void writeExcel(HttpServletResponse response, String fileName, Class<?> clazz, List<?> data) {
        write(response, fileName, clazz, data, false);
    }


    /**
     * 写Excel
     *
     * @param response response
     * @param fileName 文件名
     * @param clazz    类
     * @param data     数据
     * @param template 是否导入模板
     */
    @SneakyThrows
    private static void write(HttpServletResponse response, String fileName, Class<?> clazz, List<?> data, boolean template) {
        //定义输出文件头信息输出类型
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        //文件名称
        fileName = URLEncoder.encode(fileName, "UTF-8");
        //文件下载方式
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), clazz).sheet()
                .includeColumnFieldNames(getExcelProperty(clazz, template))
                .registerWriteHandler(new SnailSheetWriteHandler(template))
                .registerWriteHandler(new SnailRowWriteHandler())
                .registerWriteHandler(new SnailCellWriteHandler())
                .doWrite(data);
    }


    /**
     * 获取有ExcelProperty的字段属性
     *
     * @param clazz    导出对象
     * @param template 是否导出模板
     * @return 字段属性
     */
    @SneakyThrows
    public static List<String> getExcelProperty(Class<?> clazz, boolean template) {
        //获取属性
        Field[] fields = clazz.getDeclaredFields();
        List<String> property = new ArrayList<>();
        for (Field field : fields) {
            //是否有注解
            Excel annotation = field.getAnnotation(Excel.class);
            if (annotation == null) {
                continue;
            }
            //导出模板
            if (!annotation.template() && template) {
                continue;
            }
            property.add(field.getName());
        }
        //父类的属性
        Class<?> superclass = clazz.getSuperclass();
        if (superclass == null) {
            return property;
        }
        List<String> excelProperty = getExcelProperty(superclass, template);
        if (CollectionUtil.isNotEmpty(excelProperty)) {
            property.addAll(excelProperty);
        }
        return property;
    }

    @SneakyThrows
    public static Map<Integer, Head> getHeadMap(Class<?> clazz, Map<Integer, Head> headMap, AnalysisContext context) {
        Map<Integer, Head> map = new HashMap<>();
        //获取属性
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //是否有注解
            Excel annotation = field.getAnnotation(Excel.class);
            if (annotation == null) {
                continue;
            }
            //导出模板
            if (!annotation.template()) {
                continue;
            }
            Class<? extends Converter<?>> converterClazz = annotation.converter();
            if(converterClazz != null){
                context.currentReadHolder().converterMap().computeIfAbsent(ConverterKeyBuild.buildKey(field.getType(), CellDataTypeEnum.STRING), key -> {
                    try {
                        return converterClazz.getDeclaredConstructor().newInstance();
                    } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                             NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                });
            }
            int index = annotation.index();
            //获取当前属性在headMap的
            Integer indexKey = headMap.keySet().stream().filter(key -> headMap.get(key).getFieldName().equals(field.getName())).findFirst().orElse(-1);
            map.put(index, headMap.get(indexKey));
        }
        //父类的属性
        Class<?> superclass = clazz.getSuperclass();
        if (superclass != null) {
            map.putAll(getHeadMap(superclass, headMap,context));
        }
        return map;
    }
    /**
     * 读取Excel数据
     *
     * @param multipartFile Excel文件
     */
    @SneakyThrows
    public static void readExcel(MultipartFile multipartFile, Class<?> clazz, IExcelService<?> service) {
        EasyExcel.read(multipartFile.getInputStream(), clazz, service).sheet().doRead();
    }
}
